/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.rpc.api.spi;

import io.iec.edp.caf.rpc.api.entity.RpcContext;
import lombok.var;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

public interface RpcContextResolver {
    /**
     * 判断是否需要本地线程隔离
     * @param requsetTenantId 请求里的租户id
     * @param serviceUnitName su名称
     * @return
     */
    boolean resolveIsolated(Integer requsetTenantId, String serviceUnitName);

    /**
     * 获取request里的租户信息
     * @param msu
     * @param context
     * @return
     */
    Integer resolveRequestTargetTenantId(String msu, HashMap<String, String> context);

    /**
     * 校验RpcContext
     * @param context
     */
    void validateRpcContext(RpcContext context);

    /**
     * 获取当前系统的RpcContextResolver
     * @return
     */
    static RpcContextResolver getRpcContextResolver(){
        return RpcContextResolver.RpcContextResolverSelector.rpcContextResolver;
    }

    static RpcContext captureAll(){
        RpcContext context = RpcContext.getInstance();
        Map<String, RpcContextTransmittee> rpcContextTransmiteeMap = RpcContextTransmitteSelector.rpcContextTransmiteeMap;
        rpcContextTransmiteeMap.values().forEach(x->{
            context.merge(x.capture());
        });
        return context;
    }

    static void replayAll(RpcContext context){
        Map<String, RpcContextTransmittee> rpcContextTransmiteeMap = RpcContextTransmitteSelector.rpcContextTransmiteeMap;
        rpcContextTransmiteeMap.values().forEach(x->{
            x.replay(context);
        });
    }

    static void restoreAll(RpcContext context){
        Map<String, RpcContextTransmittee> rpcContextTransmiteeMap = RpcContextTransmitteSelector.rpcContextTransmiteeMap;
        rpcContextTransmiteeMap.values().forEach(x->{
            x.restore(context);
        });
    }

    static RpcContext capture(String ...transmitteTypes){
        RpcContext context = RpcContext.getInstance();
        Map<String, RpcContextTransmittee> rpcContextTransmiteeMap = RpcContextTransmitteSelector.rpcContextTransmiteeMap;
        for (String transmitteType : transmitteTypes) {
            var transmitter = rpcContextTransmiteeMap.get(transmitteType);
            if(transmitter!=null){
                context = context.merge(transmitter.capture());
            }
        }
        return context;
    }
    static void replay(RpcContext context,String ...transmitteTypes){
        Map<String, RpcContextTransmittee> rpcContextTransmiteeMap = RpcContextTransmitteSelector.rpcContextTransmiteeMap;
        for (String transmitteType : transmitteTypes) {
            var transmitter = rpcContextTransmiteeMap.get(transmitteType);
            if(transmitter!=null){
                transmitter.replay(context);
            }
        }
    }
    static void restore(RpcContext context,String ...transmitteTypes){
        Map<String, RpcContextTransmittee> rpcContextTransmiteeMap = RpcContextTransmitteSelector.rpcContextTransmiteeMap;
        for (String transmitteType : transmitteTypes) {
            var transmitter = rpcContextTransmiteeMap.get(transmitteType);
            if(transmitter!=null){
                transmitter.replay(context);
            }
        }
    }

    class RpcContextResolverSelector{
        private static RpcContextResolver rpcContextResolver = null;
        static {
            Log log = LogFactory.getLog(RpcContextResolver.class);
//            Map<ServiceCenterProviderType,ServiceCenterProvider> serviceCenterProviderMap = new HashMap<>();
            ServiceLoader<RpcContextResolver> resolvers = ServiceLoader.load(RpcContextResolver.class);
            //默认RpcContextResolver只允许有一个
            for (RpcContextResolver resolver : resolvers) {
                if(log.isDebugEnabled()){
                    log.debug("current RpcContextResolver ["+resolver.getClass().getName()+"]");
                }
                rpcContextResolver = resolver;
            }

            Assert.notNull(rpcContextResolver, "[RpcContextResolver] is null, please check.");
        }
    }
    class RpcContextTransmitteSelector{
        private static Map<String,RpcContextTransmittee> rpcContextTransmiteeMap = new HashMap<>();
        static {
            Log log = LogFactory.getLog(RpcContextTransmittee.class);
//            Map<ServiceCenterProviderType,ServiceCenterProvider> serviceCenterProviderMap = new HashMap<>();
            ServiceLoader<RpcContextTransmittee> resolvers = ServiceLoader.load(RpcContextTransmittee.class);
            //默认ServiceCenterProvider只允许有一个
            for (RpcContextTransmittee resolver : resolvers) {
                if(log.isDebugEnabled()){
                    log.debug("current RpcContextTransmittee ["+resolver.getClass().getName()+"]");
                }
                rpcContextTransmiteeMap.put(resolver.identify(),resolver);
            }
            if(log.isDebugEnabled()){
                log.debug("RpcContextTransmittee size ["+rpcContextTransmiteeMap.keySet().size()+" ], All RpcContextTransmittee ["+rpcContextTransmiteeMap.keySet().toArray().toString()+"]");
            }
            Assert.notEmpty(rpcContextTransmiteeMap,"[RpcContextTransmittee Map] is empty map, please check.");
            Assert.noNullElements(rpcContextTransmiteeMap.keySet(),"[RpcContextTransmittee Map] has null value, please check.");
        }
    }

}
